using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using Microsoft.VisualStudio.Modeling;

using EastBancTech.DSL.FileSystemDefinition.Util;
using System.Collections;

namespace EastBancTech.DSL.FileSystemDefinition.Custom
{
    public delegate bool InclusionDecision(String filePath);

    public delegate void SetupFileProperty(FileModel file);
    
    public class FileSystemBuilder
    {
        public static void BuildFileSystemModelFromPath(FileSystemModel model, String rootFolder)
        {
            BuildFileSystemModelFromPath(model, rootFolder, new InclusionDecision(InclusionDecisions.IncludeAll), null);
        }

        //public static void BuildFileSystemModelFromPath(FileSystemModel model, String rootFolder, String[] includePaterns, String[] excludePaterns)
        //{
        //    BuildFileSystemModelFromPath(model, rootFolder, includePaterns, excludePaterns, null);
        //}

        public static void BuildFileSystemModelFromPath(FileSystemModel model, String rootFolder, String[] includePaterns, String[] excludePaterns, SetupFileProperty setupPropertyCallback)
        {
            model.Directories.Add(new DirectoryModel(model.Partition,
                new PropertyAssignment(DirectoryModel.NameDomainPropertyId, model.Name)));
            
            model.Path = rootFolder;
            
            if(!Path.IsPathRooted(rootFolder))
                rootFolder = new DirectoryInfo(Path.Combine(System.Environment.CurrentDirectory, rootFolder)).FullName;
                                   
            DirectoryScanner scanner = new DirectoryScanner();
            scanner.Includes.AddRange(includePaterns);
            scanner.Excludes.AddRange(excludePaterns);
            scanner.BaseDirectory = new DirectoryInfo(rootFolder);
            scanner.Scan();
            
            foreach (string fileName in scanner.FileNames)
            {
                String relativeFilePath = Native.FileSystemInterop.GetRelativePath(fileName, rootFolder);
                DirectoryModel currentDir = GetDirectory(model.Directories[0], Path.GetDirectoryName(relativeFilePath));
                FileModel file = CreateFile(model.Partition, fileName);
                currentDir.Files.Add(file);
            }
        }

        private static DirectoryModel CreateDirectory(Microsoft.VisualStudio.Modeling.Partition partition, String dName)
        {
            DirectoryModel f = new DirectoryModel(partition);
            f.Name = dName;
            return f;
        }

        private static DirectoryModel GetDirectory(DirectoryModel root, String path)
        {
            if (String.IsNullOrEmpty(path))
                return root;

            String[] directories = path.Split(Path.DirectorySeparatorChar);
            DirectoryModel currentDir = root;

            foreach (String folderName in directories)
            {
                foreach (DirectoryModel dir in currentDir.SubDirectories)
                    if (String.Compare(dir.Name, folderName, true) == 0)
                    {
                        currentDir = dir;
                        break;
                    }

                if (String.Compare(currentDir.Name, folderName, true) != 0)
                {
                    DirectoryModel f = CreateDirectory(root.Partition, folderName);
                    currentDir.SubDirectories.Add(f);
                    currentDir = f;
                }
            }

            return currentDir;
        }

        private static FileModel CreateFile(Microsoft.VisualStudio.Modeling.Partition partition, String fName)
        {
            FileModel file = null;

            try
            {
                if (String.Compare(Path.GetExtension(fName), ".dll", true) == 0 ||
                    String.Compare(Path.GetExtension(fName), ".exe", true) == 0)
                {
                    AssemblyName asmName = AssemblyName.GetAssemblyName(fName);
                    AssemblyFileModel asm = new AssemblyFileModel(partition);
                    asm.AssemblyName = asmName.Name;
                    asm.AssemblyVersion = asmName.Version.ToString();
                    asm.Culture = asmName.CultureInfo.Name;
                    if (string.IsNullOrEmpty(asm.Culture))
                    {
                        asm.Culture = "neutral";
                    }
                    asm.ProcessorArchitecture = asmName.ProcessorArchitecture;

                    if ((AssemblyNameFlags.PublicKey & asmName.Flags) != AssemblyNameFlags.None)
                    {
                        asm.PublicKeyToken = String.Empty;
                        foreach (byte b in asmName.GetPublicKeyToken())
                        {
                            asm.PublicKeyToken += b.ToString("X2");
                        }
                    }

                    file = asm;
                }
                else
                {
                    file = new FileModel(partition);
                }
            }
            catch (BadImageFormatException)
            {
                file = new FileModel(partition);
            }

            FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(fName);
            file.FileVersion = fileVersionInfo.FileVersion != null ? fileVersionInfo.FileVersion.ToString() : null;
            file.Name = Path.GetFileName(fileVersionInfo.FileName);

            FileAttributes attrs = File.GetAttributes(fName);
            file.System = (attrs & FileAttributes.System) == FileAttributes.System;
            file.ReadOnly = (attrs & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
            file.Hidden = (attrs & FileAttributes.Hidden) == FileAttributes.Hidden;

            return file;
        }


        public static void BuildFileSystemModelFromPath(FileSystemModel model, String rootFolder, InclusionDecision inclusionDecisionCallback, SetupFileProperty setupPropertyCallback)
        {
            model.Directories.Add(new DirectoryModel(model.Partition,
                new PropertyAssignment(DirectoryModel.NameDomainPropertyId, "SourceDir")));

            ProcessFolder(model.Directories[0], rootFolder, rootFolder, inclusionDecisionCallback);
            
        }

        protected virtual DirectoryModel CreateDirectoryModelFromPath(String path)
        {
            return null;
        }

        protected virtual FileModel CreateFileModelFromPath(String filePath)
        {
            return null;
        }

        static private void ProcessFolder(DirectoryModel dirMod, String folderPath, String rootDir, InclusionDecision inclusionDecisionCallback)
        {
            foreach (String fName in Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly))
            {
                String relativeFilePath = Native.FileSystemInterop.GetRelativePath(fName, rootDir);

                if (inclusionDecisionCallback(relativeFilePath))
                {
                    dirMod.Files.Add(CreateFile(dirMod.Partition, fName));
                }
            }

            foreach (String dName in Directory.GetDirectories(folderPath, "*", SearchOption.TopDirectoryOnly))
            {
                DirectoryModel f = new DirectoryModel(dirMod.Store);

                ProcessFolder(f, dName, rootDir, inclusionDecisionCallback);

                if (f.Files.Count == 0 && f.SubDirectories.Count == 0)
                {
                    f.Delete();
                    continue;
                }

                f.Name = dName.Substring(dName.LastIndexOf('\\') + 1);

                dirMod.SubDirectories.Add(f);
            }
        }
    }

    public class InclusionDecisions
    {
        static public bool IncludeAll(String fileName)
        {
            return true;
        }
    }

    //internal class PatternDecisionMaker
    //{
    //    protected List<String> _includePatterns;
    //    protected List<String> _excludePatterns;
    //    protected List<Regex> _includeRegexPattern;
    //    protected List<Regex> _excludeRegexPattern;

    //    public PatternDecisionMaker()
    //        :this(null, null)
    //    {
    //    }

    //    public PatternDecisionMaker(List<String> includePatterns, List<String> excludePatterns)
    //    {
    //        _includePatterns = includePatterns;
    //        _excludePatterns = excludePatterns;

    //        _includeRegexPattern = new List<Regex>();
    //        _excludePatterns = new List<string>();

    //        foreach (String s in _includePatterns)
    //            _includeRegexPattern.Add(ConvertFilePatternToRegex(s));

    //        foreach (String s in _excludePatterns)
    //            _excludeRegexPattern.Add(ConvertFilePatternToRegex(s));
    //    }

    //    public PatternDecisionMaker(List<Regex> includePatterns, List<Regex> excludePatterns)
    //    {
    //        _includeRegexPattern = includePatterns;
    //        _excludeRegexPattern = excludePatterns;
    //    }

    //    public bool PatternDecisionMakerCalback(String fileName)
    //    {
    //        if (_patterns == null)
    //            return true;

    //        foreach (Regex reg in _excludeRegexPattern)
    //            if (reg.Match(fileName).Success)
    //                return false;

    //        foreach (Regex reg in _includeRegexPattern)
    //            if (reg.Match(fileName).Success)
    //                return true;

    //        return false;
    //    }

    //    internal static Regex ConvertFilePatternToRegex(String filePattern)
    //    {
    //        String pattern = filePattern;
    //        if (filePattern.Length == 0)
    //            return null;

    //        pattern = pattern.Replace(@"\", "/");
    //        pattern = pattern.Replace(".", "[.]");
    //        pattern = pattern.Replace("?", @"[^\/]+");
    //        pattern = pattern.Replace("*", @"[^\/]*");
    //        pattern = pattern.Replace("//", "/.*/");

    //        return new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
    //    }
    //}
}
